package com.motu.monstercity.module.common.network;

import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Message.Builder;
import com.motu.monstercity.module.common.protobuf.ProtobufManager;
import com.motu.monstercity.protocol.CmdEnum.EnumCmdID;
import com.motu.monstercity.protocol.CsGameSystem.CSGameSystemLogicErrorResponse;
import com.motu.monstercity.protocol.CsGameSystem.CSGameSystemLogicErrorResponseType;
import com.motu.vertx.module.utility.monitor.MonitorManager;
import com.motu.vertx.module.utility.threadpool.DBLogManager;
import com.motu.vertx.module.utility.toolset.ErrorWord;
import com.motu.vertx.module.utility.toolset.ErrorWordManager;
import com.motu.vertx.module.utility.toolset.Tool;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.ServerWebSocket;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ResponseUtils {
	private static final Logger logger = LoggerFactory.getLogger(ResponseUtils.class);
	public static final int PUSH_TIPS_TYPE_1 = 1;// 服务端给某个玩家推送提示信息的类型1：大世界行军助力
	public static final int PUSH_TIPS_TYPE_2 = 2;// 服务端给某个玩家推送提示信息的类型1：医院治疗伤兵助力
	public static final int PUSH_TIPS_TYPE_3 = 3;// 服务端给某个玩家推送提示信息的类型 发布的联姻信息被对方联姻成功
	public static final int PUSH_TIPS_TYPE_999 = 999;// 广播给联盟其他成员 同步战力和城市收益，联盟建筑对城市收益有加成，联盟地标对战力有加成


	/**
	 * 发送http请求响应
	 *
	 * @param context   http请求上下文
	 * @param response  下行响应数据
	 * @param reqPacket 请求数据包
	 */
	public static void send(RoutingContext context, Builder response, DataDecoded reqPacket) {
		int cmdCode = reqPacket.getCmdCode();
		byte[] resData = response.build().toByteArray();

		Buffer buffer = DataEncoded.encode(cmdCode, (byte)0, resData, reqPacket.getMessageId());
		context.response().putHeader("Content-Type", "application/x-protobuf;charset=utf-8");
		context.response().end(buffer);

		// 监控协议数据
		MonitorManager.addProtocolMonitorDataHttp(cmdCode, resData, reqPacket.getReqData());
	}

	/**
	 * 发送失败http请求响应
	 *
	 * @param context   http请求上下文
	 * @param response  下行响应数据
	 * @param reqPacket 请求数据包
	 * @param errorWord 错误码
	 */
	public static void sendError(RoutingContext context, Builder response, DataDecoded reqPacket, ErrorWord errorWord) {
		sendError(context, response, reqPacket, errorWord, null, null);
	}

	/**
	 * 发送失败http请求响应
	 *
	 * @param context   http请求上下文
	 * @param response  下行响应数据
	 * @param reqPacket 请求数据包
	 * @param errorWord 错误码
	 * @param errMsg    自定义错误描述
	 * @param params    自定义参数
	 */
	public static void sendError(RoutingContext context, Builder response, DataDecoded reqPacket, ErrorWord errorWord, String errMsg, String params) {
		// 获取错误描述
		int cmdCode = reqPacket.getCmdCode();
		String cmdName = ProtobufManager.getNameByCode(cmdCode);
		errMsg = errMsg != null ? errMsg : ErrorWordManager.get(cmdName, errorWord, reqPacket.getLanguage());

		// 给错误码和错误描述赋值
		FieldDescriptor errorCodeField = response.getDescriptorForType().findFieldByName("ret");
		FieldDescriptor errorMessageField = response.getDescriptorForType().findFieldByName("errMsg");
		response.setField(errorCodeField, errorWord.value);
		response.setField(errorMessageField, errMsg);

		send(context, response, reqPacket);

		// 记录错误日志
		DBLogManager.addErrorCode(0, cmdName, params, errorWord, cmdCode, reqPacket.getReqData());
	}

	/**
	 * 游戏服返回给网关服的响应数据
	 *
	 * @param cmdCode 协议编号
	 * @param data    协议数据 最顶层级的数据
	 */
	public static JsonObject getResponseData(int cmdCode, byte[] data) {
		return new JsonObject().put("code", cmdCode).put("data", data);
	}

	// 服务端推送给客户端的数据 最顶层级的数据
	public static JsonObject getPushResponseData(byte[] data) {
		return getResponseData(EnumCmdID.CMD_GAME_SYSTEM_PUSH_DATA_VALUE, data);
	}

	// 服务端推送给客户端的数据 派对数据 最顶层级的数据
	public static JsonObject getFeastResponseData(byte[] data) {
		return getResponseData(EnumCmdID.CMD_GAME_FEAST_PUSH_DATA_VALUE, data);
	}

	// 服务端推送给客户端的数据 红点数据 最顶层级的数据
	public static JsonObject getRedpointResponseData(byte[] data) {
		return getResponseData(EnumCmdID.CMD_GAME_SYSTEM_RED_POINT_VALUE, data);
	}

	// 服务端推送给客户端的数据 聊天数据 最顶层级的数据
	public static JsonObject getChatResponseData(byte[] data) {
		return getResponseData(EnumCmdID.CMD_GAME_CHAT_MSG_VALUE, data);
	}

	// 服务端推送给客户端的提示信息
	public static JsonObject getPushTipsResponseData(byte[] data) {
		return getResponseData(EnumCmdID.CMD_GAME_SYSTEM_PUSH_TIPS_VALUE, data);
	}

	// 服务端推送给客户端的数据 世界Boss 顶层级的数据
	public static JsonObject getWorldBossResponseData(byte[] data) {
		return getResponseData(EnumCmdID.CMD_GAME_WORLD_BOSS_PUSH_TOP_VALUE, data);
	}

	/**
	 * 游戏服返回给网关服的失败响应数据
	 */
	public static JsonObject getResponseError(long userId, int cmdCode, String cmdName, ErrorWord errorWord, byte[] request, int language, int messageID) {
		return getResponseError(userId, cmdCode, cmdName, errorWord, request, language, messageID, null, null);
	}

	/**
	 * 游戏服返回给网关服的失败响应数据
	 */
	public static JsonObject getResponseError(long userId, int cmdCode, String cmdName, ErrorWord errorWord, byte[] request, int language, int messageID, CSGameSystemLogicErrorResponseType type, String param) {
		CSGameSystemLogicErrorResponse.Builder response = CSGameSystemLogicErrorResponse.newBuilder();
		response.setCmdCode(cmdCode);
		response.setNetIndex(messageID);
		response.setRet(errorWord.value);
		response.setErrMsg(ErrorWordManager.get(cmdName, errorWord, language));
		if (type != null) {
			response.setType(type);
		}
		if (param != null) {
			response.setParam(param);
		}

		// 记录错误日志
		DBLogManager.addErrorCode(userId, cmdName, param, errorWord, cmdCode, request);

		return getResponseData(EnumCmdID.CMD_GAME_SYSTEM_LOGIC_ERROR_VALUE, response.build().toByteArray());
	}

	/**
	 * 发送失败socket请求响应
	 *
	 * @param socket    socket连接对象
	 * @param reqPacket 请求数据包
	 * @param errorWord 错误码
	 */
	public static void sendError(ServerWebSocket socket, DataDecoded reqPacket, ErrorWord errorWord) {
		sendError(socket, reqPacket, errorWord, true);// 默认需要记录错误日志
	}

	/**
	 * 发送失败socket请求响应
	 *
	 * @param socket    socket连接对象
	 * @param reqPacket 请求数据包
	 * @param errorWord 错误码
	 * @param log       记录错误日志
	 */
	public static void sendError(ServerWebSocket socket, DataDecoded reqPacket, ErrorWord errorWord, boolean log) {
		sendError(socket, reqPacket, errorWord, null, null, null, false, log);
	}

	/**
	 * 发送失败socket请求响应
	 *
	 * @param socket    socket连接对象
	 * @param reqPacket 请求数据包
	 * @param errorWord 错误码
	 * @param errMsg    自定义错误描述
	 * @param type      错误扩展类型
	 * @param param     错误扩展参数
	 * @param log       记录错误日志
	 */
	public static void sendError(ServerWebSocket socket, DataDecoded reqPacket, ErrorWord errorWord, String errMsg, CSGameSystemLogicErrorResponseType type, String param, boolean log) {
		sendError(socket, reqPacket, errorWord, errMsg, type, param, false, log);
	}

	/**
	 * 发送失败socket请求响应并且关闭连接
	 *
	 * @param socket    socket连接对象
	 * @param reqPacket 请求数据包
	 * @param errorWord 错误码
	 */
	public static void sendErrorAndClose(ServerWebSocket socket, DataDecoded reqPacket, ErrorWord errorWord) {
		sendErrorAndClose(socket, reqPacket, errorWord, true);// 默认需要记录错误日志
	}

	/**
	 * 发送失败socket请求响应并且关闭连接
	 *
	 * @param socket    socket连接对象
	 * @param reqPacket 请求数据包
	 * @param errorWord 错误码
	 * @param log       记录错误日志
	 */
	public static void sendErrorAndClose(ServerWebSocket socket, DataDecoded reqPacket, ErrorWord errorWord, boolean log) {
		sendError(socket, reqPacket, errorWord, null, null, null, true, log);
	}

	/**
	 * 发送失败socket请求响应并且关闭连接
	 *
	 * @param socket    socket连接对象
	 * @param reqPacket 请求数据包
	 * @param errorWord 错误码
	 * @param errMsg    自定义错误描述
	 * @param type      错误扩展类型
	 * @param param     错误扩展参数
	 * @param log       记录错误日志
	 */
	public static void sendErrorAndClose(ServerWebSocket socket, DataDecoded reqPacket, ErrorWord errorWord, String errMsg, CSGameSystemLogicErrorResponseType type, String param, boolean log) {
		sendError(socket, reqPacket, errorWord, errMsg, type, param, true, log);
	}

	/**
	 * 发送失败socket请求响应
	 *
	 * @param socket    socket连接对象
	 * @param reqPacket 请求数据包
	 * @param errorWord 错误码
	 * @param errMsg    自定义错误描述
	 * @param type      错误扩展类型
	 * @param param     错误扩展参数
	 * @param close     是否关闭连接
	 * @param log       记录错误日志
	 */
	private static void sendError(ServerWebSocket socket, DataDecoded reqPacket, ErrorWord errorWord, String errMsg, CSGameSystemLogicErrorResponseType type, String param, boolean close, boolean log) {
		String cmdName = ProtobufManager.getNameByCode(reqPacket.getCmdCode());
		errMsg = errMsg != null ? errMsg : ErrorWordManager.get(cmdName, errorWord, reqPacket.getLanguage());

		CSGameSystemLogicErrorResponse.Builder response = CSGameSystemLogicErrorResponse.newBuilder();
		response.setCmdCode(reqPacket.getCmdCode());
		response.setNetIndex(reqPacket.getMessageId());
		response.setRet(errorWord.value);
		response.setErrMsg(errMsg);
		if (type != null) {
			response.setType(type);
		}
		if (param != null) {
			response.setParam(param);
		}

		if (close) {
			sendAndClose(socket, EnumCmdID.CMD_GAME_SYSTEM_LOGIC_ERROR_VALUE, response.build().toByteArray(), reqPacket);
		} else {
			send(socket, EnumCmdID.CMD_GAME_SYSTEM_LOGIC_ERROR_VALUE, response.build().toByteArray(), reqPacket);
		}

		// 记录错误日志
		if (log) {
			DBLogManager.addErrorCode(0, cmdName, null, errorWord, reqPacket.getCmdCode(), reqPacket.getReqData());
		}
	}

	/**
	 * 发送socket请求响应并关闭连接
	 *
	 * @param socket    socket连接对象
	 * @param cmdCode   协议号
	 * @param resData   下行响应数据
	 * @param reqPacket 请求数据包
	 */
	private static void sendAndClose(ServerWebSocket socket, int cmdCode, byte[] resData, DataDecoded reqPacket) {
		try {
			if (!socket.isClosed()) {
				socket.end(DataEncoded.encode(cmdCode, (byte) 0, resData, reqPacket.getMessageId()));
			}
		} catch (Exception e) {
			logger.error("sendResponseAndClose error:{}", Tool.getException(e));
		}
	}

	/**
	 * 发送socket请求响应
	 *
	 * @param socket    socket连接对象
	 * @param cmdCode   协议号
	 * @param resData   下行响应数据
	 * @param reqPacket 请求数据包
	 */
	public static void send(ServerWebSocket socket, int cmdCode, byte[] resData, DataDecoded reqPacket) {
		try {
			if (!socket.isClosed()) {
				byte[] data = doLanguage(socket, cmdCode, resData);
				if (data != null) {
					socket.writeFinalBinaryFrame(DataEncoded.encode(cmdCode, (byte)0, data, reqPacket.getMessageId()));
				}
			}
		} catch (Exception e) {
			logger.error("sendResponse error:{}", Tool.getException(e));
		}
	}

	/**
	 * 服务端推送socket信息
	 *
	 * @param socket  socket连接对象
	 * @param cmdCode 请求协议码
	 * @param resData 下行响应数据
	 */
	public static void push(ServerWebSocket socket, int cmdCode, byte[] resData) {
		try {
			if (!socket.isClosed()) {
				byte[] data = doLanguage(socket, cmdCode, resData);
				if (data != null) {
					socket.writeFinalBinaryFrame(DataEncoded.encode(cmdCode, (byte)0, data, 0));
				}
			}
		} catch (Exception e) {
			logger.error("sendResponse error:{}", Tool.getException(e));
		}
	}

	/**
	 * 多语言处理
	 */
	private static byte[] doLanguage(ServerWebSocket socket, int cmdCode, byte data[]) {
//        if (cmdCode == EnumCmdID.CMD_GAME_SYSTEM_ROLLNOTICE_VALUE) {// 推送滚动公告
//            try {
//                int language = SocketInfo.getLanguage(socket);
//                if (language == 0) {
//                    return data;
//                }
//                CSGameSystemRollnoticeResponse response = CSGameSystemRollnoticeResponse.parseFrom(data);
//                String content = WordManager.getWordByLanguage(response.getContent(), language);
//                if (Tool.isEmpty(content)) {
//                    return null;
//                }
//                if (response.getContent().equals(content)) {
//                    return data;
//                } else {
//                    CSGameSystemRollnoticeResponse.Builder builder = response.toBuilder();
//                    builder.setContent(content);
//                    return builder.build().toByteArray();
//                }
//            } catch (Exception e) {
//                logger.error(Tool.getException(e));
//            }
//        }
		return data;
	}

}
